#!/usr/bin/perl -w
# collate-test: Collate testcases from several source files.
#
# Copyright (C) 2007,2008,2011,2015 Olly Betts
# Copyright (C) 2008 Lemur Consulting Ltd
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

use strict;

my $generated_warning =
"/* Warning: This file is generated by $0 - do not modify directly! */\n";

my %by_cond = ();
my $srcdir = shift @ARGV;
my $collated_hdr = shift @ARGV;
my $allfile = shift @ARGV;

open COLLATED_HDR, ">$collated_hdr.tmp" or die $!;
print COLLATED_HDR "$generated_warning\n";

open ALL, ">$allfile.tmp" or die $!;
print ALL $generated_warning;

for my $cc_source (@ARGV) {
    (my $generated_h = $cc_source) =~ s/\.\w+$/\.h/;
    print ALL "#include \"$generated_h\"\n";

    -f $cc_source or $cc_source = "$srcdir/$cc_source";

    open HDR, ">$generated_h.tmp" or die $!;
    print HDR $generated_warning;
    open SRC, "<$cc_source" or die $!;
    my $ignore = 0;
    my @if_stack;
    while (<SRC>) {
	if (!$ignore && /^DEFINE_TESTCASE\((.*?),\s*(.*)\)/) {
	    my ($test_func, $cond) = ($1, $2);
	    $cond =~ s/\s+//g;
	    push @{$by_cond{$cond}}, $test_func;
	    print HDR "extern void test_$test_func();\n";
	}
        if (s/^#\s*//) {
	    s!/\*.*?\*/! !g;
	    s!//.*!!;
	    if (/^if\s*0\s*$/) {
		push @if_stack, $ignore;
	        $ignore = 1;
	    } elsif (/^if/) {
		push @if_stack, $ignore;
	    } elsif (/^endif/) {
		$ignore = pop @if_stack;
	    }
	}
    }
    close SRC;
    close HDR or die $!;
    update_if_required($generated_h);
}

foreach my $cond (sort keys %by_cond) {
#	my $name = $cond;
#	$name =~ s/\&\&/_and_/g;
#	$name =~ s/\|\|/_or_/g;
#	$name =~ s/\!/not_/g;
#	$name =~ s/\(/bra_/g;
#	$name =~ s/\)/_ket/g;
    my $c_cond = $cond;
    $c_cond =~ s/\b([a-z]+)\b/(properties&\U$1\E)/g;
    print COLLATED_HDR <<END;
    if ($c_cond) {
	static const test_desc tests[] = {
END
    for (@{$by_cond{$cond}}) {
	print COLLATED_HDR <<END;
	    { \"$_\", test_$_ },
END
    }
    print COLLATED_HDR <<END;
	    { 0, 0 }
	};
	result = max(result, test_driver::run(tests));
    }
END
}

close ALL or die $!;
update_if_required($allfile);

close COLLATED_HDR or die $!;
update_if_required($collated_hdr);

# If $file.tmp is different to $file, rename it over the top.  Otherwise
# just delete it to avoid needlessly updating $file's timestamp.
sub update_if_required {
    my $file = shift;
    my $current_size = -s $file;
    if (defined $current_size && $current_size == -s "$file.tmp") {
	open OLD, "<$file" or die $!;
	open NEW, "<$file.tmp" or die $!;
	my $different = 0;
	while (<OLD>) {
	    my $new = <NEW>;
	    if ($_ ne $new) {
		$different = 1;
		last;
	    }
	}
	close OLD;
	close NEW;
	if (! $different) {
	    unlink "$file.tmp";
	    return;
	}
    }
different:
    unlink $file if $^O eq 'MSWin32' && defined $current_size;
    rename "$file.tmp", $file or dir $!;
}
